#include "FluidBB.h"

// Virtools 'Write In Texture' helpers

#define CKPGUID_WRITEMODE CKDEFINEGUID(0x675e3903,0x7a06003c)


struct intcolor {
	intcolor() {};
	intcolor(DWORD col) {
		r = ColorGetRed(col);
		g = ColorGetGreen(col);
		b = ColorGetBlue(col);
		a = ColorGetAlpha(col);
	}
	intcolor& operator += (const intcolor& v) {
		r += v.r;
		g += v.g;
		b += v.b;
		a += v.a;
		return *this;
	}
	intcolor& operator *= (int v) {
		r *= v;
		g *= v;
		b *= v;
		a *= v;
		return *this;
	}
	intcolor& operator /= (int v) {
		r /= v;
		g /= v;
		b /= v;
		a /= v;
		return *this;
	}
	intcolor& operator >>= (int v) {
		r >>= v;
		g >>= v;
		b >>= v;
		a >>= v;
		return *this;
	}
	operator DWORD() {
		return RGBAITOCOLOR(r,g,b,a);
	}

	void Normalize() {
		if(r < 0) r = 0;
		else if(r>255) r = 255;
		if(g < 0) g = 0;
		else if(g>255) g = 255;
		if(b < 0) b = 0;
		else if(b>255) b = 255;
		if(a < 0) a = 0;
		else if(a>255) a = 255;
	}
	int r,g,b,a;
};


void SetPixelReplace(float* DstPtr, float Color)
{
	*DstPtr = Color;
}

void SetPixelBlend(float* DstPtr, float Color)
{
	float c1 = *DstPtr;
	float c2 = Color;
	c1 += c2;
	c1 /= 2;
	*DstPtr=c1;
}

void SetPixelAddSat(float* DstPtr, float Color)
{
	float c1 = *DstPtr;
	float c2 = Color;
	c1+=c2;
	
	if (c1 > 1.0f)
		c1 = 1.0f;
	else if (c1 < 0.0f)
		c1 = 0.0f;

	*DstPtr = c1;
}

void SetPixelAdd(float* DstPtr, float Color)
{
	float c1 = *DstPtr;
	float c2 = Color;

	c1 += c2;
	*DstPtr = c1;
}


typedef void (*SetPixelFunction)(float* DstPtr, float Color);

SetPixelFunction SetPixelFct[4]={SetPixelReplace,SetPixelBlend,SetPixelAddSat,SetPixelAdd};

// END Virtools 'Write In Texture' helpers


////////////////////////////////////////
// Building block
////////////////////////////////////////

int WriteTexture(const CKBehaviorContext& behcontext)
{
	CKBehavior* beh = behcontext.Behavior;

	beh->ActivateInput(0, FALSE);	// deactivation


	// Virtools 'Write In Texture' modified code

	Vx2DVector uv;
	beh->GetInputParameterValue(0,&uv);
	//VxColor col;
	//beh->GetInputParameterValue(1,&col);
	int size=1;
	beh->GetInputParameterValue(1,&size);
	CKTexture* pen_tex=(CKTexture*)beh->GetInputParameterObject(2);
	int WriteMode=0;
	beh->GetInputParameterValue(3,&WriteMode);

/*
	XString dbg;
	dbg += WriteMode;
	beh->GetCKContext()->OutputToConsole((CKSTRING)dbg.CStr());
*/
	//----- Ensure Texture Coordinates are valid
	int tmp;
	if (uv.x < 0.0f)
	{
		tmp = (int)uv.x;
		uv.x = -uv.x + tmp;
	}
	else if (uv.x > 1.0f)
	{
		tmp = (int)uv.x;
		uv.x -= tmp;
	}

	if (uv.y < 0.0f)
	{
		tmp = (int)uv.y;
		uv.y = -uv.y + tmp;
	}
	else if (uv.y > 1.0f)
	{
		tmp = (int)uv.y;
		uv.y -= tmp;
	}


//#define SetPixel(x,y,scol) dens_prev[y*((N*4)>>2)+x]=scol

	//VxImageDescEx TexDesc;
	//tex->GetSystemTextureDesc(TexDesc);
	//DWORD* ptr=(DWORD*)tex->LockSurfacePtr();
	//DWORD dcol=RGBAFTOCOLOR(&col);
	//if (ptr && (TexDesc.BitsPerPixel==32))
	//{

	float *ptr = dens_prev;
	int sx = (int)(uv.x * (N + 2)), sy = (int)(uv.y * (N + 2));

	if (pen_tex) 
	{
		VxImageDescEx PenTexDesc;
		DWORD* sptr=(DWORD*)pen_tex->LockSurfacePtr();
		pen_tex->GetSystemTextureDesc(PenTexDesc);
		if (sptr && PenTexDesc.BitsPerPixel == 32) 
		{
			int WidthOn2 = PenTexDesc.Width / 2;
			int HeightOn2 = PenTexDesc.Height / 2;
			int x0 = sx - WidthOn2;		int y0 = sy - HeightOn2;
			int x1 = sx + WidthOn2-1;		int y1 = sy + HeightOn2 - 1;
			if (x0 < 0) { sptr += (-x0); x0 = 0; }
			if (y0 < 0) { sptr += (-y0 * PenTexDesc.Width); y0 = 0; }
			if (x1 >= N)	x1 = (N + 2) - 1;
			if (y1 >= N)	y1 = (N + 2) - 1;

			// Go to first pixel
			int cwidth = ((x1 - x0) + 1);
			ptr += y0 * (N + 2);
			for (int i = y0; i <= y1; ++i, sptr += PenTexDesc.Width, ptr += (N + 2))
				for (int j = 0; j < cwidth; ++j)
				{
					SetPixelFct[WriteMode](ptr + j + x0, (float)ColorGetAlpha(sptr[j]) / 255.0f);
				}
		}
	}
	/*
		else
			if (size==1) SetPixelFct[WriteMode](&ptr[sy*TexDesc.Width+sx],dcol);
			else
			{
				int x0=sx-size;		int y0=sy-size;
				int x1=sx+size;		int y1=sy+size;
				if (x0<0) x0=0;
				if (y0<0) y0=0;
				if (x1>=TexDesc.Width)	x1=TexDesc.Width-1;
				if (y1>=TexDesc.Height)	y1=TexDesc.Height-1;

				ptr+=TexDesc.Width*y0+x0;
				for (int j=y0;j<=y1;++j,ptr+=TexDesc.Width) {
					DWORD* ptr2=ptr; 
					for (int i=x0;i<=x1;++i,ptr2++)
						SetPixelFct[WriteMode](ptr2,dcol);
				}
			}
	}
	tex->ReleaseSurfacePtr();
	*/

	// END Virtools 'Write In Texture' modified code


	beh->ActivateOutput(0);

	return (CKBR_OK);
}

CKERROR CreateWriteTextureProto ( CKBehaviorPrototype **pproto)
{
	CKBehaviorPrototype *proto = CreateCKBehaviorPrototype("WriteTexture");

	if(!proto)
		return (CKERR_OUTOFMEMORY);

	proto->DeclareInput("In");
	proto->DeclareOutput("Out");

	proto->DeclareInParameter("UV", CKPGUID_2DVECTOR);
	proto->DeclareInParameter("Size", CKPGUID_INT,"1");
	proto->DeclareInParameter("Pen Texture", CKPGUID_TEXTURE);
	proto->DeclareInParameter("Write Mode", CKPGUID_WRITEMODE);

	proto->SetFlags(CK_BEHAVIORPROTOTYPE_NORMAL);
	proto->SetFunction(WriteTexture);
	//proto->SetBehaviorFlags(CKBEHAVIOR_TARGETABLE);

	*pproto = proto;
	return (CK_OK);
}

CKObjectDeclaration *FillBehaviorWriteTexture()
{
	CKObjectDeclaration *od = CreateCKObjectDeclaration ( "WriteTexture" );
	od->SetDescription ( "cf. 'Write In Texture' BB" );
	od->SetCategory ( "Fluids" );
	od->SetType ( CKDLL_BEHAVIORPROTOTYPE );
	od->SetGuid ( WRITETEXTURE_GUID );
	od->SetAuthorGuid ( AUTHOR_GUID );
	od->SetAuthorName ( "Jonathan DERROUGH" );
	od->SetVersion ( 0x00000001 );
	od->SetCreationFunction ( CreateWriteTextureProto );
	//od->SetCompatibleClassId ( CKCID_3DENTITY );
	return od;
}
